﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Sockets;
using Kurax.BoardGames.Server.Engine;
using Kurax.BoardGames.Server.Engine.Commands;

namespace Kurax.BoardGames.Server {
    public class Connection {
        private bool isStopPending;

        public TcpClient Client {
            get;
            private set;
        }

        public GameContext Context {
            get;
            private set;
        }

        private void ProcessCommandData(byte[] data) {
            if(data.Length <= 8 || data[0] != 2 || data[data.Length - 1] != 3)
                return;

            var level0 = data[3];
            var level1 = data[4];
            var level2 = data[5];
            var level3 = data[6];

            CommandBase command = null;
            switch(level0) {
                case 0:
                    command = SystemCommands.GetCommand(level1, level2, level3);
                    break;
            }
            if(command == null)
                return;

            var responseData = command.Execute(this.Context, data.Skip(7).Take(data.Length - 8).ToArray());
            if(responseData == null || responseData.Length == 0)
                return;

            var response = new List<byte> {
                0x2
            };
            response.AddRange(new[] {
                data[1], data[2]
            });
            response.AddRange(responseData);
            response.Add(0x3);

            var stream = this.Client.GetStream();
            stream.Write(response.ToArray(), 0, response.Count);
        }

        public Connection(TcpClient client) {
            if(client == null)
                throw new ArgumentNullException("client");

            this.Client = client;
            this.Context = new GameContext {
                Connection = this,
            };
        }

        public void Run() {
            var result = new List<byte>(this.Client.ReceiveBufferSize);
            var buffer = new byte[this.Client.ReceiveBufferSize];
            while(true) {
                if(this.isStopPending) {
                    this.Client.Close();
                    this.isStopPending = false;
                    break;
                }
                try {
                    var stream = this.Client.GetStream();
                    var count = stream.Read(buffer, 0, buffer.Length);
                    while(count > 0) {
                        result.AddRange(buffer.Take(count));
                        count = stream.DataAvailable ? stream.Read(buffer, 0, buffer.Length) : 0;
                    }
                    this.ProcessCommandData(result.ToArray());
                    result.Clear();
                } catch(IOException) {
                    return;
                }
            }
        }

        public void Stop() {
            this.isStopPending = true;
        }
    }
}
